package com.clp.protocol.iec104.apdu;

import com.clp.protocol.iec104.apdu.asdu.Cot;
import com.clp.protocol.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.apdu.asdu.Vsq;
import com.clp.protocol.iec104.apdu.asdu.infoobj.InfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.NcInfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.*;
import com.clp.protocol.iec104.apdu.asdu.infoobj.qua.*;
import com.clp.protocol.iec104.definition.Sq;
import com.clp.protocol.iec104.definition.Tc;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.iec104.definition.cot.Cause;
import com.clp.protocol.iec104.definition.cot.Pn;
import com.clp.protocol.iec104.definition.cot.Test;
import com.clp.protocol.iec104.definition.quatype.InitCauseQuaType;
import com.clp.protocol.iec104.definition.quatype.TaQuaType;
import com.clp.protocol.iec104.definition.quatype.TotalCallQuaType;
import com.clp.protocol.iec104.definition.quatype.TpQuaType;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Asdu工厂
 */
public class IAsduFactory {

    public static IAsdu getIAsdu(TypeTag typeTag, Vsq vsq, Cot cot, int rtuAddr, List<InfoObj> infoObjs) {
        // 构建apci，长度设为0，在转为字节之后再赋值
        return new IAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    /**
     * 获取apdu：i帧，总召唤
     *
     * @param rtuAddr：公共地址
     * @return
     */
    public static IAsdu getIAsduOfTotalCall100(int rtuAddr) {
        TypeTag typeTag = TypeTag.C_IC_NA_1; // 类型标识为总召唤100
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN位0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, 0, null, new C_IC_NA_1_Qua(TotalCallQuaType.GROUP_TOTAL_CALL), null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall100Ack(int rtuAddr, Pn pn) {
        TypeTag typeTag = TypeTag.C_IC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, pn, Cause.COT_ACTCON);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(
                typeTag, 0, null, new C_IC_NA_1_Qua(TotalCallQuaType.GROUP_TOTAL_CALL), null)
        );

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall101(int rtuAddr) {
        TypeTag typeTag = TypeTag.C_CI_NA_1; // 类型标识为总召唤101
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN位0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, 0, null, new C_CI_NA_1_Qua(TpQuaType.QCC), null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall101Ack(int rtuAddr, Pn pn) {
        TypeTag typeTag = TypeTag.C_CI_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, pn, Cause.COT_ACTCON);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, 0, null, new C_CI_NA_1_Qua(TpQuaType.QCC), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfOnePointTc(int rtuAddr, int infoObjAddr, Tc.CmdType cmdType, Tc.OnePointSwitch onePointSwitch) {
        TypeTag typeTag = TypeTag.C_SC_NA_1; // 类型标识为单点命令，遥控
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN为0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, infoObjAddr, new C_SC_NA_1_InfoElem(cmdType, onePointSwitch), null, null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfOnePointTcAck(int rtuAddr, int infoObjAddr, Tc.CmdType cmdType, Tc.OnePointSwitch onePointSwitch, Pn pn) {
        TypeTag typeTag = TypeTag.C_SC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, pn, Cause.COT_ACTCON);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_SC_NA_1_InfoElem(cmdType, onePointSwitch), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfOnePointTcFinished(int rtuAddr, int infoObjAddr, Tc.OnePointSwitch onePointSwitch) {
        TypeTag typeTag = TypeTag.C_SC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACTTERM);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_SC_NA_1_InfoElem(Tc.CmdType.EXECUTE, onePointSwitch), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTwoPointTc(int rtuAddr, int infoObjAddr, Tc.CmdType cmdType, Tc.TwoPointSwitch twoPointSwitch) {
        TypeTag typeTag = TypeTag.C_DC_NA_1; // 类型标识为双点命令，遥控
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN为0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, infoObjAddr, new C_DC_NA_1_InfoElem(cmdType, twoPointSwitch), null, null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTwoPointTcAck(int rtuAddr, int infoObjAddr, Tc.CmdType cmdType, Tc.TwoPointSwitch twoPointSwitch, Pn pn) {
        TypeTag typeTag = TypeTag.C_DC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, pn, Cause.COT_ACTCON);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_DC_NA_1_InfoElem(cmdType, twoPointSwitch), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTwoPointTcFinished(int rtuAddr, int infoObjAddr, Tc.TwoPointSwitch twoPointSwitch) {
        TypeTag typeTag = TypeTag.C_DC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACTTERM);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_DC_NA_1_InfoElem(Tc.CmdType.EXECUTE, twoPointSwitch), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTaNormalized(int rtuAddr, int infoObjAddr, int setVal, TaQuaType quaType) {
        TypeTag typeTag = TypeTag.C_SE_NA_1; // 设定值命令，归一化值
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN为0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, infoObjAddr, new C_SE_NA_1_InfoElem(setVal), new C_SE_NA_1_Qua(quaType), null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTaFloat(int rtuAddr, int infoObjAddr, float setVal, TaQuaType quaType) {
        TypeTag typeTag = TypeTag.C_SE_NC_1; // 设定值命令，短浮点数
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1); // 非顺序，数量1
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACT); // 测试位0，PN为0，传输原因：激活
        List<InfoObj> infoObjs = new ArrayList<>();
        InfoObj infoObj = new NcInfoObj(typeTag, infoObjAddr, new C_SE_NC_1_InfoElem(setVal), new C_SE_NC_1_Qua(quaType), null);
        infoObjs.add(infoObj);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTaFloatAck(int rtuAddr, int infoObjAddr, float floatVal, TaQuaType quaType, Pn pn) {
        TypeTag typeTag = TypeTag.C_SE_NC_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, pn, Cause.COT_ACTCON);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_SE_NC_1_InfoElem(floatVal), new C_SE_NC_1_Qua(quaType), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTaFloatFinished(int rtuAddr, int infoObjAddr, float floatVal) {
        TypeTag typeTag = TypeTag.C_SE_NC_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACTTERM);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new C_SE_NC_1_InfoElem(floatVal), new C_SE_NC_1_Qua(TaQuaType.EXECUTE), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfInitCompleted(int rtuAddr, InitCauseQuaType quaType) {
        TypeTag typeTag = TypeTag.M_EI_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_INIT);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, 0, null, new M_EI_NA_1_Qua(quaType), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall100TmDataFloat(int rtuAddr, List<InfoObj> infoObjs) {
        TypeTag typeTag = TypeTag.M_ME_NC_1;
        Vsq vsq = new Vsq(Sq.CONTINUE, infoObjs.size());
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_INRO);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall100TsDataOnePoint(int rtuAddr, List<InfoObj> infoObjs) {
        TypeTag typeTag = TypeTag.M_SP_NA_1;
        Vsq vsq = new Vsq(Sq.CONTINUE, infoObjs.size());
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_INRO);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall100TsDataTwoPoint(int rtuAddr, List<InfoObj> infoObjs) {
        TypeTag typeTag = TypeTag.M_DP_NA_1;
        Vsq vsq = new Vsq(Sq.CONTINUE, infoObjs.size());
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_INRO);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall100Finished(int rtuAddr) {
        TypeTag typeTag = TypeTag.C_IC_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACTTERM);

        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag,0, null,
                new C_IC_NA_1_Qua(TotalCallQuaType.GROUP_TOTAL_CALL), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall101DataCumulant(int rtuAddr, List<InfoObj> infoObjs) {
        TypeTag typeTag = TypeTag.M_IT_NA_1; // 累计量
        Vsq vsq = new Vsq(Sq.CONTINUE, infoObjs.size());
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_REQCOGEN);

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfTotalCall101Finished(int rtuAddr) {
        TypeTag typeTag = TypeTag.C_CI_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_ACTTERM);
        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, 0, null,
                new C_CI_NA_1_Qua(TpQuaType.QCC), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    /**
     * 变化遥测
     * @param rtuAddr
     * @param infoObjAddr
     * @param floatVal
     * @return
     */
    public static IAsdu getIAsduOfVaryTmFloat(int rtuAddr, int infoObjAddr, float floatVal, boolean isValid) {
        TypeTag typeTag = TypeTag.M_ME_NC_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_SPONT);

        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new M_ME_NC_1_InfoElem(floatVal), new M_ME_NC_1_Qua(isValid), null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfVaryTsOnePoint(int rtuAddr, int infoObjAddr, boolean isSwitchOn,
                                                 boolean isValid, boolean isCurrVal, boolean isReplaced, boolean isLocked) {
        TypeTag typeTag = TypeTag.M_SP_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_SPONT);

        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new M_SP_NA_1_InfoElem(isValid, isCurrVal, isReplaced, isLocked, isSwitchOn), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }

    public static IAsdu getIAsduOfVaryTsTwoPoint(int rtuAddr, int infoObjAddr, boolean isSwitchOn,
                                                 boolean isValid, boolean isCurrVal, boolean isReplaced, boolean isLocked) {
        TypeTag typeTag = TypeTag.M_DP_NA_1;
        Vsq vsq = new Vsq(Sq.NOT_CONTINUE, 1);
        Cot cot = new Cot(Test.NOT_TEST, Pn.PN_YES, Cause.COT_SPONT);

        List<InfoObj> infoObjs = Collections.singletonList(new NcInfoObj(typeTag, infoObjAddr,
                new M_DP_NA_1_InfoElem(isValid, isCurrVal, isReplaced, isLocked, isSwitchOn), null, null));

        return getIAsdu(typeTag, vsq, cot, rtuAddr, infoObjs);
    }
}
